import abc
import os
from unittest.mock import patch

from library_test.mock_manage.mock_model.mock_ansible_module import AnsibleModule
from library_test.base_test import BaseLibraryTest


class TestHccnCheck(BaseLibraryTest, metaclass=abc.ABCMeta):
    TESTCASE_DIR = os.path.join(os.path.dirname(__file__), "testcase")

    @classmethod
    def get_module_path(cls):
        return "ascend_deployer.library.check_hccn"

    @classmethod
    def setUpClass(cls) -> None:
        super().setUpClass()
        from ascend_deployer.library.check_hccn import HccnCheck
        cls._mock_get_card()
        cls._mock_run_cmd()
        cls.utils = HccnCheck()

    @classmethod
    def _mock_run_cmd(cls):
        patcher = patch(cls.get_module_path() + ".CheckUtil.run_cmd")
        mocker = patcher.start()
        mocker.return_value = "8"

    @classmethod
    def _mock_get_card(cls):
        patcher = patch(cls.get_module_path() + ".CheckUtil.get_card")
        mocker = patcher.start()
        mocker.return_value = "910b"


class TestHccnCheckGeneral(TestHccnCheck):

    @classmethod
    def get_testcase_path(cls):
        return os.path.join(cls.TESTCASE_DIR, "hccn_check_general.yml")

    def test_check_support(self):
        self.utils.check_support()
        # do not support
        self.utils.npu_name = "310P"
        with self.assertRaises(AnsibleModule.FailJson) as context:
            self.utils.check_support()
        res = self.parse_context_exception(context)
        self.assertTrue("please check your NPU card" in res.get("msg"))

    def test_check_ip(self):
        # normal
        self.utils.error_messages = []
        self.utils.check_ip()
        self.assertEqual(0, len(self.utils.error_messages))

        # device_ips
        # no device_ips
        self.utils.device_ips = []
        with self.assertRaises(AnsibleModule.FailJson) as context:
            self.utils.check_ip()
        res = self.parse_context_exception(context)
        self.assertTrue("Please configure the" in res.get("msg"))

        # 7 ips
        self.utils.device_ips = ["192.168.0.1","192.168.0.2","192.168.0.3","192.168.0.4","192.168.0.5","192.168.0.6","192.168.0.7"]
        self.utils.check_ip()
        self.assertTrue("inconsistent with the number of NPU in position" in self.utils.error_messages[0])

        # wrong ip
        self.utils.device_ips = ["192.168.0","192.168.0.2","192.168.0.3","192.168.0.4","192.168.0.5","192.168.0.6","192.168.0.7","192.168.0.8"]
        with self.assertRaises(AnsibleModule.FailJson) as context:
            self.utils.check_ip()
        res = self.parse_context_exception(context)
        self.assertTrue("is not a valid ip" in res.get("msg"))

        # combine ipv4 and ipv6
        self.utils.error_messages = []
        self.utils.device_ips = ["2001:0db8:85a3:0000:0000:8a2e:0370:7334","192.168.0.2","192.168.0.3","192.168.0.4","192.168.0.5","192.168.0.6","192.168.0.7","192.168.0.8"]
        self.utils.check_ip()
        self.assertTrue("Do not combine them" in self.utils.error_messages[0])
        #
        # NPU DO NOT support ipv6
        self.utils.npu_name = "910A1"
        self.utils.npu_count = 4
        self.utils.error_messages = []
        self.utils.device_ips = ["2001:0db8:85a3:0000:0000:8a2e:0370:7334","001:0db8:85a3:0000:0000:8a2e:0370:7335","001:0db8:85a3:0000:0000:8a2e:0370:7336","001:0db8:85a3:0000:0000:8a2e:0370:7337"]
        self.utils.check_ip()
        self.assertTrue("DO NOT support IPV6" in self.utils.error_messages[0])

        # IPV6 NOT SUPPORT - 2
        self.utils.npu_name = "910A3"
        self.utils.npu_count = 4
        self.utils.error_messages = []
        self.utils.check_ip()
        self.assertTrue("DO NOT support IPV6" in self.utils.error_messages[0])
        self.utils.npu_count = 8 # recovery the npu count

    def test_check_common_network(self):
        self.utils.error_messages = []
        # 910A2 common_network = "" or "0.0.0.0/0"
        self.utils.npu_name = "910A2"
        self.utils.common_network = ""
        self.utils.check_common_network()
        self.assertEqual(0, len(self.utils.error_messages))
        self.utils.common_network = "0.0.0.0/0"
        self.utils.check_common_network()
        self.assertEqual(0, len(self.utils.error_messages))

        # 910A1/910A2 common_network should be "0.0.0.0/0"
        self.utils.npu_name = "910A1"
        self.utils.common_network = ""
        self.utils.check_common_network()
        self.assertTrue("The common_network you configured should be '0.0.0.0/0'" in self.utils.error_messages[0])
        self.utils.error_messages = []
        self.utils.common_network = "0.0.0.0/0"
        self.utils.check_common_network()
        self.assertEqual(0, len(self.utils.error_messages))

        self.utils.npu_name = "910A3"
        self.utils.common_network = ""
        self.utils.check_common_network()
        self.assertTrue("The common_network you configured should be '0.0.0.0/0'" in self.utils.error_messages[0])
        self.utils.error_messages = []
        self.utils.common_network = "0.0.0.0/0"
        self.utils.check_common_network()
        self.assertEqual(0, len(self.utils.error_messages))

    def test_check_bitmap(self):
        # normal
        self.utils.bitmap = "0,0,0,1,0,0,0,0"
        self.utils.check_bitmap()

        # wrong length
        self.utils.bitmap = "0,0,0,1,0,0,0,0,1"
        with self.assertRaises(AnsibleModule.FailJson) as context:
            self.utils.check_bitmap()
        res = self.parse_context_exception(context)
        self.assertTrue("the length should be 8" in res.get("msg"))

        # wrong value
        self.utils.bitmap = "0,0,0,1,0,0,0,10"
        with self.assertRaises(AnsibleModule.FailJson) as context:
            self.utils.check_bitmap()
        res = self.parse_context_exception(context)
        self.assertTrue("consist of 0 and 1" in res.get("msg"))

    def test_check_dscp_tc(self):

        # normal
        self.utils.dscp_tc = "33:2,"
        self.utils.check_dscp_tc()
        self.assertEqual(0, len(self.utils.error_messages))

        # no value
        self.utils.dscp_tc = ""
        self.utils.check_dscp_tc()
        self.assertEqual(0, len(self.utils.error_messages))

        # wrong value
        self.utils.dscp_tc = "dhishaidsa"
        self.utils.check_dscp_tc()
        self.assertTrue("Please correct it and retry" in self.utils.error_messages[0])

        # corner case
        self.utils.error_messages = []
        self.utils.dscp_tc = "63:8,"
        self.utils.check_dscp_tc()
        self.assertTrue("The dscp_tc you configured in the inventory_file is not correct" in self.utils.error_messages[0])

    def test_check_gateways(self):

        # normal
        self.utils.check_gateways()

        # empty
        self.utils.gateways = []
        with self.assertRaises(AnsibleModule.FailJson) as context:
            self.utils.check_gateways()
        res = self.parse_context_exception(context)
        self.assertTrue("Please configure the gateways in inventory_file first." in res.get("msg"))

        # Wrong ip
        self.utils.gateways = ["192"]
        with self.assertRaises(AnsibleModule.FailJson) as context:
            self.utils.check_gateways()
        res = self.parse_context_exception(context)
        self.assertTrue("not a valid IP." in res.get("msg"))

    def test_check_configuration(self):
        self.utils.gateways = ["192.168.0.1"]
        self.utils.check_configuration()

        # not in the same net
        self.utils.gateways = ["10.10.1.1"]
        self.utils.check_configuration()
        self.assertTrue("are not in the same subnet." in self.utils.error_messages[0])
        self.utils.error_messages = []  # recovery


class TestHccnCheckIpv4(TestHccnCheck):

    @classmethod
    def get_testcase_path(cls):
        return os.path.join(cls.TESTCASE_DIR, "hccn_check.yml")

    def test_is_ipv6(self):
        self.assertTrue(self.utils._is_ipv6("2001:0db8:85a3:0000:0000:8a2e:0370:7334"))
        self.assertFalse(self.utils._is_ipv6("192.168.0.1"))

    def test_is_valid_netmask_ipv4(self):
        self.assertTrue(self.utils._is_valid_netmask("255.255.255.0"))
        self.assertFalse(self.utils._is_valid_netmask("255.255.255.256"))

    def test_ip_to_binary_ipv4(self):
        self.assertEqual(self.utils._ip_to_binary("192.168.1.1"), '11000000101010000000000100000001')

    def test_in_same_subnet_ipv4(self):
        self.assertTrue(self.utils._in_same_subnet("192.168.1.1", "192.168.1.100"))
        self.assertFalse(self.utils._in_same_subnet("192.168.1.1", "192.168.2.100"))


class TestHccnCheckIpv6(TestHccnCheck):
    @classmethod
    def get_testcase_path(cls):
        return os.path.join(cls.TESTCASE_DIR, "hccn_check_ipv6.yml")

    def test_in_same_subnet_ipv6(self):
        self.assertTrue(self.utils._in_same_subnet("2001:0db8:85a3:0000:0000:8a2e:0370:7334",
                                                  "2001:0db8:85a3:0000:0000:8a2e:0370:7335"))
        self.assertFalse(self.utils._in_same_subnet("2001:0db8:85a3:0000:0000:8a2e:0370:7334",
                                                   "2001:0db8:85a3:0001:0000:8a2e:0370:7335"))

    def test_ip_to_binary_ipv6(self):
        self.assertEqual(self.utils._ip_to_binary("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
                         '00100000000000010000110110111000100001011010001100000000000000000000000000000000100010100010111000000011011100000111001100110100')

    def test_is_valid_netmask_ipv6(self):
        self.assertTrue(self.utils._is_valid_netmask("::/128"))
        self.assertTrue(self.utils._is_valid_netmask("::/64"))
        self.assertFalse(self.utils._is_valid_netmask("::/129"))
